package fi.iki.joker.mobilogger.img;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import com.google.gdata.client.GoogleService;
import com.google.gdata.data.Entry;
import com.google.gdata.data.Feed;
import com.google.gdata.data.Link;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import fi.iki.joker.mobilogger.MobiLoggerConstants;
import fi.iki.joker.mobilogger.R;
import fi.iki.joker.mobilogger.blogs.BlogConfigConstants;
import fi.iki.joker.mobilogger.db.ImageRepoBean;
import fi.iki.joker.mobilogger.exception.CaptchaChallengeException;
import fi.iki.joker.mobilogger.exception.GoogleClientLoginException;
import fi.iki.joker.mobilogger.exception.PicasaWebException;
import fi.iki.joker.mobilogger.img.ImageRepositoryConstants.ImageRepositoryType;
import fi.iki.joker.mobilogger.util.AlertUtil;
import fi.iki.joker.mobilogger.util.GoogleClientLoginUtil;
import fi.iki.joker.mobilogger.util.PicasaWebUtil;
import fi.iki.joker.mobilogger.util.PicasaWebUtil.PicasaWebAlbumTO;

public class PicasaInterface implements ImageRepository, View.OnClickListener,
	AdapterView.OnItemSelectedListener {
 
	private String TAG = "PicasaInterface";
	private Activity parentRef = null;
	private Spinner userAlbums = null;
	private LinkedList<PicasaWebAlbumTO> albumData = null;
	private String[] listData = null;
	private final int FETCH_BUTTON_ID = 23478; //Magic number for the "fetch album names button"
	private String MSG_KEY = "message_key_value";
	private int currentlySelectedAlbum = -1;
	
	// The message key that's used to communicate between this class and the thread that's doing the querying is defined above
	// Below, are the status values that are used in the communication.
	
	private final int MSG_CODE_PREPARING = 1;
	private final int MSG_CODE_AUTHETICATING = 2;
	private final int MSG_CODE_CONNECTING_READ_ALBUM_FEED = 3;
	private final int MSG_CODE_HANDLING_RESPONSE = 4;
	private final int MSG_CODE_DONE_COMMUNICATING = 5;
	private final int MSG_CODE_PROMPTING_CAPTCHA = 6;
	private final int MSG_CODE_CANT_PROCEED = 7;
	
	// The GoogleClientLoginUtil has the google API specific error status codes. Below are the status codes relevant to the
	// handling of authentication from the Mobilogger perspective.
	
	private String STATUS_KEY = "status_key";
	private String STATUS_BAD_AUTH = "bad_authentication";
	private String STATUS_RESPONSE_NULL = "null_response_from_picasa_api";
	private String STATUS_FEED_PROBLEM = "exception_reading_feed";
	private String STATUS_ZERO_ALBUMS = "zero_albums_in_feed";
	private String STATUS_OK = "status_ok";
	
	/**
	 * The local integer status code that is populated based on the albums fetch thread's response value 
	 */
	
	private int fetchStatus = -1;
	
	private ProgressDialog fetchProgress = null;
	

	/**
	 * Handler for the album fetch thread. 
	 */
	
	private final Handler mHandler = new Handler () {
		@Override
		public void handleMessage(Message msg) {
			Bundle content = msg.getData();
			fetchStatus = content.getInt(MSG_KEY);
			if(fetchStatus == MSG_CODE_PREPARING) {
				fetchProgress.setMessage("Preparing API ...");
			} else if(fetchStatus == MSG_CODE_AUTHETICATING) {
				fetchProgress.setMessage("Authenticating...");
			} else if(fetchStatus == MSG_CODE_CONNECTING_READ_ALBUM_FEED) {
				fetchProgress.setMessage("Contacting server...");
			} else if(fetchStatus == MSG_CODE_DONE_COMMUNICATING) {
				fetchProgress.setMessage("Done communicating");
				if(content.getString(STATUS_KEY).equals(STATUS_ZERO_ALBUMS)) {
					AlertUtil.showAlert(parentRef, "No albums", "You don't have any albums in Picasa that are usable from Mobilogger, sorry");
				} else if(content.getString(STATUS_KEY).equals(STATUS_OK)) {
					updateSpinner(userAlbums);
				}
			} else if(fetchStatus == MSG_CODE_HANDLING_RESPONSE) {
				fetchProgress.setMessage("Extracting response...");
			} else if(fetchStatus == MSG_CODE_CANT_PROCEED) {
				fetchProgress.setMessage("Can't proceed due to error.");
				String details = "";
				if(content.getString(STATUS_KEY).equals(STATUS_FEED_PROBLEM)) {
					details = "There's a problem with picasa data feed.";
				} else if(content.getString(STATUS_KEY).equals(STATUS_RESPONSE_NULL)) {
					details = "Didn't get a valid answer from the server.";
				} else if(content.getString(STATUS_KEY).equals(STATUS_BAD_AUTH)) {
					details = "Authentication failed (check your username and password).";
				}
				AlertUtil.showAlert(parentRef, "Problem reading albums", "Application encountered error reading your albums:"+details);
				fetchProgress.dismiss();
			}
			
		}
	};
		
	final Runnable mEndfetch = new Runnable() {
		public void run () {
			endFetch();
		}	
	};
	
	public void drawConfigFields(Activity parent, TableLayout tl, 
			Context context, int startIdFrom, int populateFromID) {
		this.parentRef = parent; 
		// first row of the picasa configurator has the button which onClick()
		// triggers the fetch of albums over the web
		TableRow tr1 = new TableRow(context);
		// second table row has the label and a spinner which has the fetched albums 
		// the user has.
		TableRow tr2 = new TableRow(context);
    	tr1.setId(startIdFrom);
    	tr2.setId(startIdFrom+1);
		TextView a = new TextView(context);
		Button b = new Button(context);
		userAlbums = new Spinner(context);
		b.setText("Fetch your albums");
		b.setOnClickListener(this);
		TableRow.LayoutParams lp = new TableRow.LayoutParams();
		a.setText("Fetch your picasa albums \n(enter credentials \nfirst above):");
		updateSpinner(userAlbums);
		tr1.addView(a, lp);
		tr1.addView(b,lp);
		tr2.addView(userAlbums,lp);
		tl.addView(tr1, new TableLayout.LayoutParams());
		tl.addView(tr2, new TableLayout.LayoutParams());
	}

	public String getAuthId(String userName, String password) {
		// TODO Auto-generated method stub
		return null;
	}

	
	/**
	 * Return the interface configuration String. This is the blog's name and posting URL (id) concatenated together and |in between
	 * @return null if
	 */
	
	public String getInterfaceConfig() {
		if(albumData == null) {
			return null;
		} else {
			return albumData.get(currentlySelectedAlbum).getName()+"|"+albumData.get(currentlySelectedAlbum).getId();
		}
	}

	/**
	 * Return the enumeration what type of image repository this object enables.
	 */
	
	public ImageRepositoryType getRepositoryType() {
		return ImageRepositoryConstants.ImageRepositoryType.PICASA_WEB;
	}
	
	/**
	 * Expects a String as in getInterfaceConfig and resets the state to that.
	 * TODO: check if actually makes sense to still do this. It's inherited from blog config interface...
	 */

	public boolean setInterfaceConfig(String config) {
		// TODO Auto-generated method stub
		return false;
	}	
	
	/**
	 * Publish an image with given filename to a picasaWeb repository. The method blocks until the publishing
	 * is finished. Caller should handle the waiting in a non-GUI thread. 
	 */
	
	public String publishImage(Activity parentRef, ImageRepoBean repository,
			Uri imageUri, String fileName) {
		String username = null;
		String password = null;
		String rTitle = null;
		String postConfig = null;
		if(repository != null) {
			username = repository.getUsername();
			password = repository.getPassword();
			rTitle = repository.getTitle();
			postConfig = repository.getPostconfig();
		}
		final String publishUsername = username;
		final String publishPassword = password;
		final String publishFileName  = fileName;
		final Activity thread_parent = parentRef;
		final Uri imagePublishUri = imageUri;
		//post config is a string "[title]|[albumURL]".
		String albumUrl = postConfig.substring(postConfig.indexOf("|")+1);
		String publishUrl = albumUrl.replaceAll("entry", "feed"); 
		String authToken = "";
		GoogleClientLoginUtil gclUtil = new GoogleClientLoginUtil();
		try {
			// TODO: add captcha handling.
			String captchaToken = null;
			String captchaAnswer =null;
			authToken = gclUtil.performLogin(ImageRepositoryConstants.GOOGLE_PICASA_SERVICE_ID, publishUsername, publishPassword, captchaToken, captchaAnswer);
		} catch (GoogleClientLoginException e) {
			Log.e(TAG,"Authentication exception! "+e.getMessage());
			return null;
		} catch (CaptchaChallengeException ce) {
				Log.e(TAG,"API Requires CAPTHCA challenge response.");
		}
		Log.i(TAG, "Got authentication token from the Google ClientLogin: "+authToken);
		PicasaWebUtil picasaUtil = new PicasaWebUtil();
		String publishedImageUrl = null;
		try {
			ContentResolver cr = thread_parent.getContentResolver();
			ParcelFileDescriptor pfd = cr.openFileDescriptor(imagePublishUri, "r");
			long sizeOfImage = pfd.getStatSize();
			pfd = null;
			InputStream is = cr.openInputStream(imagePublishUri);
			publishedImageUrl = picasaUtil.uploadPhotoToAlbum(authToken, publishUrl, publishFileName, sizeOfImage, is);
		} catch (PicasaWebException pwe) {
			Log.e(TAG,"Picasa upload threw exception: "+pwe.getMessage());
		} catch (FileNotFoundException fnfe) {
			Log.e(TAG, "File corresponding to the URL "+publishUrl.toString()+" not found.");
		}
		if(publishedImageUrl == null || publishedImageUrl.equalsIgnoreCase("")) {
			Log.e(TAG,"Picasa Util returned null or empty string when publishing picture!");
			return null;		
		}  else {
			return publishedImageUrl;
		}
	}
		

	/**
	 *  OnclickListener implementation, handles the button click from the
	 *  user which starts the retrieval of configured picasa albums
	 */
	
	public void onClick(View v) {
		// TODO Auto-generated method stub
		getUserAlbums();
		return;
	}
	
	/**
	 * Implementation of the AdapterView.onItemSelectedListener
	 * handles the selection of the album entry from the populated 
	 * Spinner object.
	 */

	public void onItemSelected(AdapterView<?> adapterView, View view, int selectedItem,
			long arg3) {
		currentlySelectedAlbum = selectedItem;
		adapterView.setSelection(selectedItem);
		Log.d(TAG,"Currently selected: "+selectedItem);
		Log.d(TAG,"This is "+albumData.get(selectedItem).getName()+ " -> "+albumData.get(selectedItem).getId());
	}
	
	/**
	 * Implementation of the AdapterView.onItemSelectedListener
	 */

	public void onNothingSelected(AdapterView<?> arg0) {
		// TODO Auto-generated method stub
		currentlySelectedAlbum = 0;
		arg0.setSelection(0);
		Log.d(TAG,"Currenly selected: 0");
	}

	/**
	 * This method is used to update the Spinner view to show the user the
	 * available Picasa albums.
	 * @param list
	 */
	
	private void updateSpinner(Spinner list) { 
       	if(albumData != null) {
       		Log.d(TAG,"Starting to update spinner, the albumData is not null...");
       		list.setVisibility(View.VISIBLE);
       		Iterator<PicasaWebAlbumTO> albumIterator = albumData.iterator();
       		ArrayList<String> albumList = new ArrayList<String>();
       		while(albumIterator.hasNext()) {
       			PicasaWebAlbumTO to = albumIterator.next();
       			albumList.add(to.getName());
       		}
       		ArrayAdapter<CharSequence> res = (ArrayAdapter<CharSequence>)(new ArrayAdapter(parentRef,android.R.layout.simple_spinner_item,albumList));
       		ArrayAdapter<CharSequence> adapter = res;
       		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
       		Log.d(TAG,"Setting the adapter to handle the spinner data in to Spinner...");
       		list.setAdapter(adapter);
       		list.setSelection(0);
       		this.currentlySelectedAlbum = 0;
       		list.setOnItemSelectedListener(this);
       		Log.d(TAG, "UpdateSpinner exiting... listener set.");
       	} else {
       		this.currentlySelectedAlbum = -1;
       		list.setVisibility(View.INVISIBLE);
       	}
    }	
	
	/**
	 * Read the current username and password fields and connect to picasaweb to get the album names.
	 * This method handles threading itself and all thread code is in this class.
	 */
	
	private void getUserAlbums() {
		if(parentRef == null) {
			Log.e(TAG, "Error: Can't get the image galleries without a valid reference to invoking activity!");
			throw new IllegalStateException("Cannot continue with null parent!");			
		}
		String username = null;
		String password = null;
		EditText usernameView = (EditText)parentRef.findViewById(R.id.imagerepo_username);
		if(usernameView.getText() == null || usernameView.getText().length() < 1 ) {
			AlertUtil.showAlert(parentRef,"Username needed","You need to give your Google username in order to continue!");
			return;
		} else {
			username = usernameView.getText().toString();
		}
		usernameView = null;
		EditText passwordView = (EditText)parentRef.findViewById(R.id.imagerepo_password);
		if(passwordView.getText() == null || passwordView.getText().length() < 1) {
			AlertUtil.showAlert(parentRef,"Password needed", "You need to give your Google password in order to continue!");
			return;
		} else {
			password = passwordView.getText().toString();
		}
		final String fetchUsername = username;
		final String fetchPassword = password;
		fetchProgress = ProgressDialog.show(parentRef, "Fetching your albums", "Starting to fetch albums...");
		Thread fetchPicasaAlbums = new Thread() {
			public void run() {
				Bundle status = new Bundle();
				String authToken = "";
				Message statusMsg = mHandler.obtainMessage();
				status.putInt(MSG_KEY, MSG_CODE_PREPARING);
				statusMsg.setData(status);
				mHandler.sendMessage(statusMsg);
				GoogleClientLoginUtil gclUtil = new GoogleClientLoginUtil();
				statusMsg = mHandler.obtainMessage();
				status.putInt(MSG_KEY,MSG_CODE_AUTHETICATING);
				statusMsg.setData(status);
				mHandler.sendMessage(statusMsg);
				try {
					// TODO: add captcha handling.
					String captchaToken = null;
					String captchaAnswer =null;
					authToken = gclUtil.performLogin(ImageRepositoryConstants.GOOGLE_PICASA_SERVICE_ID, fetchUsername, fetchPassword, captchaToken, captchaAnswer);
				} catch (GoogleClientLoginException e) {
					Log.e(TAG,"Authentication exception! "+e.getMessage());
					statusMsg = mHandler.obtainMessage();
					status.putString(STATUS_KEY,STATUS_BAD_AUTH);
					status.putInt(MSG_KEY,MSG_CODE_CANT_PROCEED);
					statusMsg.setData(status);
					mHandler.sendMessage(statusMsg);
					return;
				} catch (CaptchaChallengeException ce) {
					Log.e(TAG,"API Requires CAPTHCA challenge response.");
				}
				Log.i(TAG, "Got authentication token from the Google ClientLogin: "+authToken);
				statusMsg = mHandler.obtainMessage();
				status.putInt(MSG_KEY, MSG_CODE_CONNECTING_READ_ALBUM_FEED);
				statusMsg.setData(status);
				mHandler.sendMessage(statusMsg);
				PicasaWebUtil picasaUtil = new PicasaWebUtil();
				LinkedList<PicasaWebAlbumTO> albumList = new LinkedList<PicasaWebAlbumTO>();
				try {
					albumList = picasaUtil.getAlbums(authToken);
				} catch (PicasaWebException pwe) {
					Log.e(TAG,"Picasa getAlbums threw exception: "+pwe.getMessage());
					statusMsg = mHandler.obtainMessage();
					status.putString(STATUS_KEY, STATUS_FEED_PROBLEM);
					status.putInt(MSG_KEY, MSG_CODE_CANT_PROCEED);
					mHandler.sendMessage(statusMsg);
				}
				if(albumList == null) {
					Log.e(TAG,"Picasa Util returned null when doing getAlbums!");
					statusMsg = mHandler.obtainMessage();
					status.putString(STATUS_KEY, STATUS_RESPONSE_NULL);
					status.putInt(MSG_KEY, MSG_CODE_CANT_PROCEED);
					mHandler.sendMessage(statusMsg);
				} else if(albumList.size() == 0) {
					Log.i(TAG,"Could not get any album TOs back from the PicasaUtil.getAlbums!");
					statusMsg = mHandler.obtainMessage();
					status.putString(STATUS_KEY, STATUS_ZERO_ALBUMS);
					status.putInt(MSG_KEY, MSG_CODE_DONE_COMMUNICATING);
					mHandler.sendMessage(statusMsg);
				} else { // the case that we actually got something back
					Log.i(TAG, "PicasaUtil.getAlbums returned "+albumList.size()+" albums.");
					statusMsg = mHandler.obtainMessage();
					status.putString(STATUS_KEY, STATUS_OK);
					status.putInt(MSG_KEY, MSG_CODE_DONE_COMMUNICATING);
					mHandler.sendMessage(statusMsg);
					albumData = (LinkedList<PicasaWebAlbumTO>) albumList.clone();
					albumList.clear();
					albumList = null;
				}
				mHandler.post(mEndfetch);
			}
		}; // end Thread
		fetchPicasaAlbums.start();
		fetchProgress.setMessage("Started to fetch your albums...");
		
		
	}
	
	private void endFetch() {
		fetchProgress.dismiss();
		updateSpinner(userAlbums);
	}
		
}

